# LCD基础驱动库
# 来源：https://gitcode.com/mirrors/dhylands/python_lcd
class LcdApi:
    # 控制命令
    LCD_CLR = 0x01              # 清屏
    LCD_HOME = 0x02             # 光标归位
    
    # 输入模式
    LCD_ENTRY_MODE = 0x04       # 设置输入模式
    LCD_ENTRY_INC = 0x02        # 地址递增
    LCD_ENTRY_SHIFT = 0x01      # 显示整体移位
    
    # 显示控制
    LCD_ON_CTRL = 0x08          # 显示开
    LCD_ON_DISPLAY = 0x04       # 开启显示
    LCD_ON_CURSOR = 0x02        # 显示光标
    LCD_ON_BLINK = 0x01         # 光标闪烁
    
    # 光标/显示移位
    LCD_MOVE = 0x10             # 移动光标/显示
    LCD_MOVE_DISP = 0x08        # 移动显示
    LCD_MOVE_RIGHT = 0x04       # 向右移动
    
    # 功能设置
    LCD_FUNCTION = 0x20         # 功能设置
    LCD_FUNCTION_8BIT = 0x10    # 8位接口
    LCD_FUNCTION_2LINES = 0x08  # 2行显示
    LCD_FUNCTION_10DOTS = 0x04  # 5x10点阵
    LCD_FUNCTION_RESET = 0x30   # 复位
    
    # CGRAM地址设置
    LCD_CGRAM = 0x40            # 设置CGRAM地址
    LCD_DDRAM = 0x80            # 设置DDRAM地址
    
    def __init__(self, num_lines, num_columns):
        self.num_lines = num_lines
        self.num_columns = num_columns
        self.cursor_x = 0
        self.cursor_y = 0
        self.backlight = True
        self.display_off()
        self.backlight_on()
        self.clear()
        self.hal_write_command(self.LCD_ENTRY_MODE | self.LCD_ENTRY_INC)
        self.hide_cursor()
        self.display_on()
    
    def clear(self):
        # 清屏并复位光标
        self.hal_write_command(self.LCD_CLR)
        self.hal_write_command(self.LCD_HOME)
        self.cursor_x = 0
        self.cursor_y = 0
    
    def show_cursor(self):
        # 显示光标
        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY | self.LCD_ON_CURSOR)
    
    def hide_cursor(self):
        # 隐藏光标
        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)
    
    def blink_cursor_on(self):
        # 开启光标闪烁
        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY | self.LCD_ON_BLINK)
    
    def blink_cursor_off(self):
        # 关闭光标闪烁
        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)
    
    def display_on(self):
        # 开启显示
        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)
    
    def display_off(self):
        # 关闭显示
        self.hal_write_command(self.LCD_ON_CTRL)
    
    def backlight_on(self):
        # 开启背光
        self.backlight = True
        self.hal_backlight_on()
    
    def backlight_off(self):
        # 关闭背光
        self.backlight = False
        self.hal_backlight_off()
    
    def move_to(self, cursor_x, cursor_y):
        # 移动光标到指定位置
        self.cursor_x = cursor_x
        self.cursor_y = cursor_y
        addr = cursor_x & 0x3F
        if cursor_y & 1:
            addr += 0x40    # 第二行地址
        if cursor_y & 2:
            addr += 0x14    # 第三行地址
        if cursor_y & 3:
            addr += 0x54    # 第四行地址
        self.hal_write_command(self.LCD_DDRAM | addr)
    
    def putchar(self, char):
        # 输出单个字符
        if char == '\n':
            if self.cursor_y < self.num_lines - 1:
                self.move_to(0, self.cursor_y + 1)
            else:
                self.move_to(0, 0)
        else:
            self.hal_write_data(ord(char))
            self.cursor_x += 1
            if self.cursor_x >= self.num_columns:
                self.putchar('\n')
    
    def putstr(self, string):
        # 输出字符串
        for char in string:
            self.putchar(char)
    
    def custom_char(self, location, charmap):
        # 自定义字符（5x8点阵）
        location &= 0x07
        self.hal_write_command(self.LCD_CGRAM | (location << 3))
        self.hal_sleep_us(40)
        for i in range(8):
            self.hal_write_data(charmap[i])
            self.hal_sleep_us(40)
        self.move_to(self.cursor_x, self.cursor_y)
    
    def hal_write_command(self, cmd):
        # 硬件抽象层：需在子类实现
        raise NotImplementedError
    
    def hal_write_data(self, data):
        # 硬件抽象层：需在子类实现
        raise NotImplementedError
    
    def hal_backlight_on(self):
        # 硬件抽象层：需在子类实现
        raise NotImplementedError
    
    def hal_backlight_off(self):
        # 硬件抽象层：需在子类实现
        raise NotImplementedError
    
    def hal_sleep_us(self, usecs):
        # 硬件抽象层：需在子类实现
        import time
        time.sleep_us(usecs)
